这次的 Flutter 小技巧是 ListView 和 PageView 的花式嵌套,不同 Scrollable 的嵌套冲突问题相信大家不会陌生,今天就通过 ListView 和 PageView 的三种嵌套模式带大家收获一些不一样的小技巧。
正常嵌套最常见的嵌套应该就是横向 PageView 加纵向 ListView 的组合,一般情况下这个组合不会有什么问题,除非你硬是要斜着滑。
最近刚好遇到好几个人同时在问:“斜滑 ListView 容易切换到 PageView 滑动” 的问题,如下 GIF 所示,当用户在滑动ListView时,滑动角度带上倾斜之后,可能就会导致滑动的是PageView而不是 ListView 。
虽然从我个人体验上并不觉得这是个问题,但是如果产品硬是要你修改,难道要自己重写 PageView的手势响应吗?
我们简单看一下,不管是 PageView还是ListView 它们的滑动效果都来自于 Scrollable ,而Scrollable 内部针对不同方向的响应,是通过 RawGestureDetector 完成:
VerticalDragGestureRecognizer 处理垂直方向的手势HorizontalDragGestureRecognizer 处理水平方向的手势所以简单看它们响应的判断逻辑,可以看到一个很有趣的方法 computeHitSlop : 根据 pointer 的类型确定当然命中需要的最小像素,触摸默认是 kTouchSlop (18.0)。
看到这你有没有灵光一闪:如果我们把 PageView 的 touchSlop 修改了,是不是就可以调整它响应的灵敏度? 恰好在 computeHitSlop 方法里,它可以通过 DeviceGestureSettings 来配置,而 DeviceGestureSettings 来自于 MediaQuery ,所以如下代码所示:
body: MediaQuery( ///调高 touchSlop 到 50 ,这样 pageview 滑动可能有点点影响, ///但是大概率处理了斜着滑动触发的问题 data: MediaQuery.of(context).copyWith( gestureSettings: DeviceGestureSettings(touchSlop: 50, )), child: PageView(scrollDirection: Axis.horizontal,pageSnapping: true,children: [ HandlerListView(), HandlerListView(),], ),),小技巧一:通过嵌套一个 MediaQuery ,然后调整 gestureSettings 的 touchSlop 从而修改 PageView 的灵明度 ,另外不要忘记,还需要把 ListView 的touchSlop 切换会默认 的 kTouchSlop :
class HandlerListView extends StatefulWidget{ @override _MyListViewState createState() => _MyListViewState();}class _MyListViewState extends State { @override Widget build(BuildContext context) {return MediaQuery( ///这里 touchSlop 需要调回默认 data: MediaQuery.of(context).copyWith( gestureSettings: DeviceGestureSettings(touchSlop: kTouchSlop, )), child: ListView.separated(itemCount: 15,itemBuilder: (context, index) { return ListTile(title: Text('Item $index'), );},separatorBuilder: (context, index) { return const Divider(thickness: 3, );}, ),); }}最后我们看一下效果,如下 GIF 所示,现在就算你斜着滑动,也很触发 PageView 的水平滑动,只有横向移动时才会触发PageView 的手势,当然, 如果要说这个粗暴的写法有什么问题的话,大概就是降低了PageView响应的灵敏度。
同方向 PageView 嵌套 ListView介绍完常规使用,接着来点不一样的,在垂直切换的 PageView 里嵌套垂直滚动的 ListView , 你第一感觉是不是觉得不靠谱,为什么会有这样的场景?
对于产品来说,他们不会考虑你如何实现的问题,他们只会拍着脑袋说淘宝可以,为什么你不行,所以如果是你,你会怎么做?
而关于这个需求,社区目前讨论的结果是:把 PageView 和 ListView 的滑动禁用,然后通过 RawGestureDetector 自己管理。
如果对实现逻辑分析没兴趣,可以直接看本小节末尾的 源码